/**
 * This class generates the chart for the data streams.
 */
package org.hackystat.ui.telemetryviewer.client;

import org.timepedia.chronoscope.client.XYDataset;
import org.timepedia.chronoscope.client.browser.ChartPanel;
import org.timepedia.chronoscope.client.browser.Chronoscope;

import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.WindowResizeListener;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.DockPanel;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;

/**
 * This class generates the chart for the data streams.
 * @author David J. Nickles
 *
 */
public class ChartFactory implements WindowResizeListener, ClickListener {

  /** {@literal} GOLDEN_RATIO chart ratio constant. */
  private static final double GOLDEN_RATIO = 1.618;
  /** {@literal} RIGHTCHARTOFFSET extended offset from the right for the chart pane. */
  private static final int RIGHTCHARTOFFSET = Styles.RIGHTOFFSET + 20;

  private Button clearButton;
  private ChartPanel chartPanel;
  private DockPanel titlePanel;
  private HorizontalPanel messagePanel;
  private WaitPanel waitPanel;
  
  /**
   * Constructor a valid chart.
   */
  public ChartFactory() {
    Window.addWindowResizeListener(this);
  }
  
  /**
   * Get a reference to the single RPC service.
   * @return a singular reference to a RPCServiceFactory that this class maintains.
   */
  public ChartPanel getChartPanel() {
    return this.chartPanel;
  }
  
  /**
   * Display the message about waiting.
   * @param message to display.
   */
  public void showWaiting(String message) {
    RootPanel.get(CompositeWidget.CHART_HEADER_ELEMENT_ID).clear();
    RootPanel.get(CompositeWidget.CHART_ELEMENT_ID).clear();
    RootPanel.get(CompositeWidget.CHART_ELEMENT_ID).add(this.waitPanel = new WaitPanel(message));
    this.waitPanel.setWidth(Integer.toString(Window.getClientWidth() - Styles.RIGHTOFFSET) 
      + Styles.PIXELSUFFIX);
  }
  
  /**
   * Display the message in the chart area of the page.
   * @param message to display.
   */
  public void report(String message) {
    messagePanel = new HorizontalPanel();
    messagePanel.setStyleName(Styles.BOTTOMPANEL);
    HTML messagelabel = new HTML("<b>Chart Messages:</b><br />" + message);
    messagelabel.setStyleName(Styles.EXAMPLELABEL);
    messagePanel.add(messagelabel);
    messagePanel.setCellHorizontalAlignment(messagelabel, HasHorizontalAlignment.ALIGN_CENTER);
    messagePanel.setWidth(Integer.toString(Window.getClientWidth() - Styles.RIGHTOFFSET) 
      + Styles.PIXELSUFFIX);
    RootPanel.get(CompositeWidget.CHART_ELEMENT_ID).add(messagePanel);
  }
  
  /**
   * This method gets data from the XYDatasetFactory and creates a chart in the web page, replacing
   * the one that is currently there if there is one.
   */
  public void chart() {
    RootPanel.get(CompositeWidget.CHART_HEADER_ELEMENT_ID).clear();
    RootPanel.get(CompositeWidget.CHART_ELEMENT_ID).clear();
    RootPanel.get("help").setVisible(true);

    int width = Window.getClientWidth() - RIGHTCHARTOFFSET;
    int height = (int) ((width - RIGHTCHARTOFFSET) / GOLDEN_RATIO );
    
    FlowPanel flowPanel = new FlowPanel();
    flowPanel.setSize(Integer.toString(width) + Styles.PIXELSUFFIX, 
      Integer.toString(height) + Styles.PIXELSUFFIX);
    flowPanel.setStyleName(Styles.MIDDLEPANEL);
    
    width = Window.getClientWidth() - RIGHTCHARTOFFSET;
    height = (int) ((width - RIGHTCHARTOFFSET) / GOLDEN_RATIO );
    Chronoscope.setFontBookRendering(true);
    //Chronoscope.setFontBookServiceEndpoint(GWT.getModuleBaseURL() + FONT_SERVLET_PATH);
    //Chronoscope.setFontBookServiceEndpoint("http://api.timepedia.org/api/widget/fr");
    XYDataset[] cDataset = XYDatasetFactory.getInstance().getDataset();
    if (cDataset == null) {
      flowPanel.add(
        new Label("TV internal error: the dataset is null and was not setup correctly."));
    }
    else {
      if (cDataset.length == 0) {
        flowPanel.add(new Label("No data was reported for in the specified date interval."));
      }
      else {
        this.titlePanel = new DockPanel();
        this.titlePanel.setStyleName(Styles.TOPPANEL);
        this.titlePanel.setWidth(Integer.toString(Window.getClientWidth() - RIGHTCHARTOFFSET) 
          + Styles.PIXELSUFFIX);
        StringBuffer titleBuffer = new StringBuffer(256);
        titleBuffer.append("Hackystat Telemetry Data Streams:");
        titleBuffer.append(Styles.NEWLINE);
        for (int i = 0; i < cDataset.length; i++) {
          if (cDataset[i] != null) {
            titleBuffer.append(cDataset[i].getIdentifier());
            titleBuffer.append(Styles.NEWLINE);
          }
        }
        
        HTML title = new HTML(titleBuffer.toString());
        title.setStyleName(Styles.SMALLLABEL);
        //this.titlePanel.add(title, DockPanel.WEST);
        this.titlePanel.add(new HTML("Chart"), DockPanel.WEST);
        this.titlePanel.add(this.clearButton = new Button("Clear"), DockPanel.EAST);
        this.titlePanel.setCellHorizontalAlignment(this.clearButton, 
          HasHorizontalAlignment.ALIGN_RIGHT);
        this.clearButton.addClickListener(this);
        RootPanel.get(CompositeWidget.CHART_HEADER_ELEMENT_ID).add(this.titlePanel);
        
        this.chartPanel = Chronoscope.createTimeseriesChart(cDataset, width, height);
        this.chartPanel.setStyleName("chartPanel-slim");
        flowPanel.add(this.chartPanel);
      }
    }
    RootPanel.get(CompositeWidget.CHART_ELEMENT_ID).add(flowPanel);
  }

  /** 
   * Listens for user resizing of the window.
   * @param width of the adjusted window..
   * @param height of the adjusted window.
   */
  public void onWindowResized(int width, int height) {
    if (this.chartPanel != null) {
      //note window resizing doesn't work in Chronoscope as of v0.7
      this.chartPanel.onWindowResized(width - RIGHTCHARTOFFSET, (int) ((width - RIGHTCHARTOFFSET) 
        / GOLDEN_RATIO)); 
      this.titlePanel.setWidth(Integer.toString(width - Styles.RIGHTOFFSET) + Styles.PIXELSUFFIX);
    }
    if (this.messagePanel != null) {
      this.messagePanel.setWidth(Integer.toString(width - Styles.RIGHTOFFSET) + Styles.PIXELSUFFIX);
    }
    if (this.waitPanel != null) {
      this.waitPanel.setWidth(Integer.toString(width - Styles.RIGHTOFFSET) + Styles.PIXELSUFFIX);
    }
  }

  /**
   * This method responds to user clicks on listener-registered UI elements.
   * @param sender the widget clicked on.
   */
  public void onClick(Widget sender) {
    if (sender.equals(this.clearButton)) {
      RootPanel.get(CompositeWidget.CHART_HEADER_ELEMENT_ID).clear();
      RootPanel.get(CompositeWidget.CHART_ELEMENT_ID).clear();
      RootPanel.get("help").setVisible(false);
    }
  }
}
